package com.william.jdbcplus.core.parallel;

import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.Getter;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 *
 */
@Getter
public class ParallelExecutorService implements AutoCloseable {

    private static final String DEFAULT_NAME_FORMAT = "%d";

    private static final ExecutorService SHUTDOWN_EXECUTOR = Executors.newSingleThreadExecutor(
            ParallelExecutorThreadFactoryBuilder.build("Parallel-Executor-Engine-Closer"));

    protected ListeningExecutorService executorService;

    protected int executorSize;

    protected ParallelExecutorService(final int executorSize) {
        this(executorSize, DEFAULT_NAME_FORMAT);
    }

    public ParallelExecutorService(final int executorSize, final String nameFormat) {
        this.executorSize = executorSize;
        this.executorService = MoreExecutors.listeningDecorator(buildExecutorService(this.executorSize, nameFormat));
        MoreExecutors.addDelayedShutdownHook(executorService, 60, TimeUnit.SECONDS);
    }

    private ExecutorService buildExecutorService(final int executorSize, final String nameFormat) {
        ThreadFactory shardingThreadFactory = ParallelExecutorThreadFactoryBuilder.build(nameFormat);
        return 0 == executorSize ? Executors.newCachedThreadPool(shardingThreadFactory)
                : Executors.newFixedThreadPool(executorSize, shardingThreadFactory);
    }

    /**
     * Close executor service.
     */
    public void close() {
        SHUTDOWN_EXECUTOR.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    executorService.shutdown();
                    while (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                        executorService.shutdownNow();
                    }
                } catch (final InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }
        });
    }

}